package com.hotacorp.opencloud.common.log.aop;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hotacorp.opencloud.common.authcommon.SysUserBean;
import com.hotacorp.opencloud.common.commdata.LogBean;
import com.hotacorp.opencloud.common.log.annotation.LogAnnotation;
import com.hotacorp.opencloud.common.log.feign.LogServiceProperties;
import com.hotacorp.opencloud.common.log.feign.LoggingFeign;

import lombok.extern.slf4j.Slf4j;

/**
 * @Description:利用AOP实现调用日志服务，写入日志
 * @version 0.5
 * @since JDK1.8
 * @author lwg
 * @copyright (c) 2020. All rights reserved.
 * @date 2020年1月16日 下午3:40:13
 */
@Aspect
@Slf4j
@Component
@Order(-1) // 保证该AOP在@Transactional之前执行
public class LogAnnotationAOP {
	@Autowired
	private LoggingFeign loggingFeign;
	
	@Autowired
	private LogServiceProperties logServiceProperties;
	
	private ThreadPoolExecutor executor;

	@Around("@annotation(com.hotacorp.opencloud.common.log.annotation.LogAnnotation)")
	public Object logSave(ProceedingJoinPoint joinPoint) throws Throwable {
		// 记录开始时间
		long start = System.currentTimeMillis();
		// 获取方法参数
		String url = null;
		String httpMethod = null;
		Object result = null;
		List<Object> httpReqArgs = new ArrayList<Object>();
		LogBean logBean = new LogBean();
		logBean.setCreatetime(new Date());
    	SysUserBean userDetails = (SysUserBean) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    	if (userDetails != null) {
    		logBean.setTenantid(userDetails.getTenantid());
        	logBean.setUsername(userDetails.getUsername());
    	}
		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
		LogAnnotation logAnnotation = methodSignature.getMethod().getDeclaredAnnotation(LogAnnotation.class);
		logBean.setModule(logAnnotation.module() + ":" + methodSignature.getDeclaringTypeName() + "/" + methodSignature.getName());

		Object[] args = joinPoint.getArgs();// 参数值
		url =  methodSignature.getDeclaringTypeName() + "/"+ methodSignature.getName();
		String ip = null;
		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if (requestAttributes != null) {
			HttpServletRequest request = requestAttributes.getRequest();
			if (request != null) {
				ip = IPUtils.getIpAddr(request);
				logBean.setIp(ip);
			}
		}
		
		String params = null ;
		for (Object object : args) {
			if (object instanceof HttpServletResponse || object instanceof HttpServletRequest) {
			} else {
				httpReqArgs.add(object);
			}
		}
		try {
			params = JSONObject.toJSONString(httpReqArgs);
			if (logAnnotation.record()) logBean.setParams(params);
			// 打印请求参数参数
			log.debug("开始请求，url={} , httpMethod={}, reqData={} ", url, httpMethod, params);
		} catch (Exception e) {
			log.error("记录参数失败：{}", e.getMessage());
		}
		try {
			// 调用原来的方法
			result = joinPoint.proceed();
			logBean.setFlag(Boolean.TRUE);
			logBean.setExectime(System.currentTimeMillis() - start);
			logBean.setRemark("执行成功！");
		} catch (Exception e) {
			logBean.setFlag(Boolean.FALSE);
			logBean.setExectime(System.currentTimeMillis() - start);
			logBean.setRemark(e.getMessage());
			log.error("请求报错，url={} , httpMethod={}, reqData={} ,error ={} ", url, httpMethod, params,e.getMessage());
			throw e;
		} finally {
			executor.submit(()->{
				try {
					log.trace("日志落库开始：{}", logBean);
					if(loggingFeign!=null){
						loggingFeign.addLog(logBean).toString();
					}
					log.trace("开始落库结束：{}", logBean);
				} catch (Exception e) {
					log.error("落库失败：{}", e.getMessage());
				}
			});
			// 获取回执报文及耗时
			log.debug("请求完成, 耗时={}, resp={}:", (System.currentTimeMillis() - start), result == null ? null : JSON.toJSONString(result));
		}
		return result;
	}
	
	@PostConstruct
	void init() {
		executor = new ThreadPoolExecutor(logServiceProperties.getCorenum(), logServiceProperties.getMaxnum(), 5L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(logServiceProperties.getCachenum()), new ThreadPoolExecutor.DiscardPolicy());
	}
	
	@PreDestroy
	void exit() {
		executor.shutdown();
	}
}
